/*
 * TestEnvironment.java  0.4.0 / Oct 25, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.nunit;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;

/**
 * Test Environment.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public final class TestEnvironment {

// Package-Private Static Final Fields. ----------------------------------------

  static final String STRING_JAVA_HOME = "java.home";
  static final String STRING_CLASSPATH = " -classpath ";
  static final String STRING_JAVAW = "javaw";
  static final String STRING_JAVA = "java";
  static final String STRING_GIJ = "gij";
  static final String STRING_DOUBLE_QUOTE = "\"";
  static final String STRING_SPACE = " ";
  static final String STRING_EMPTY = "";
  static final String STRING_NO_JAVA_COMMAND = "Could not find java command";

// Private Static Fields. ------------------------------------------------------

  private static String commandStart;

// Private Constructor. --------------------------------------------------------

 /**
  * Class has no instances.
  */
  private TestEnvironment() {
    // empty constructor body
  }

// Package-Private Static Methods. ---------------------------------------------

  /**
   * Returns JVM launch command.
   *
   * @return JVM launch command
   */
  public static String getLaunchCommand() {
    if (commandStart == null) { // NOPMD
      StringBuffer sb = new StringBuffer(getJavaCommand());
      String libraryPath = getLibraryPath().trim();
      String classPath = getClassPath().trim();
      if (libraryPath.length() > 0) {
        sb.append(" -Djava.library.path=");
        sb.append(libraryPath);
      }
      if (classPath.length() > 0) {
        sb.append(STRING_CLASSPATH);
        sb.append(classPath);
      }
      sb.append(STRING_SPACE);
      commandStart = sb.toString();
    }
    return commandStart;
  }

  /**
   * Returns java command (command that starts JVM).
   *
   * @return java command
   */
  public static String getJavaCommand() { // NOPMD
    String command = System.getProperty("cute4j.test.jvm");
    if (command != null) {
      return command;
    }
    String javaHome = getJavaHome();
    if (javaHome != null) {
      javaHome = javaHome + File.separator;
      String javaName = System.getProperty("java.vm.name");
      javaName = javaName.toLowerCase(); // NOPMD NOFB
      String javaBin = javaHome + "bin" + File.separator;
      if (javaName.startsWith(STRING_JAVA)) {
        command = javaBin + STRING_JAVAW;
        if (verifyJVM(command)) {
          return command;
        }
        command = javaBin + STRING_JAVA;
        if (verifyJVM(command)) {
          return command;
        }
      } else if (javaName.startsWith("gnu")) {
        command = javaBin + STRING_GIJ;
        if (verifyJVM(command)) { // NOPMD
          return command;
        }
        command = javaHome + STRING_GIJ;
        if (verifyJVM(command)) { // NOPMD
          return command;
        }
      }
      command = javaHome + STRING_JAVA;
      if (verifyJVM(command)) {
        return command;
      }
    }
    return findJavaCommand();
  }

  /**
   * Returns java home path.
   *
   * @return java home path
   */
  public static String getJavaHome() {
    return System.getProperty(STRING_JAVA_HOME, STRING_EMPTY);
  }

  /**
   * Returns current class path.
   *
   * @return current class path
   */
  public static String getClassPath() {
    String classPath = System.getProperty("java.class.path");
    if (classPath == null) {
      return STRING_EMPTY;
    }
    if (isWinPath(classPath)) {
      classPath = STRING_DOUBLE_QUOTE + classPath + STRING_DOUBLE_QUOTE;
    }
    return classPath;
  }

  /**
   * Returns library class path.
   *
   * @return library class path
   */
  public static String getLibraryPath() {
    String libraryPath = System.getProperty("java.library.path");
    if (libraryPath == null) {
      libraryPath = STRING_EMPTY;
    } else {
      if (isWinPath(libraryPath)) {
        libraryPath = STRING_DOUBLE_QUOTE +
                      libraryPath.replaceAll(STRING_DOUBLE_QUOTE,
                                             STRING_EMPTY) +
                      STRING_DOUBLE_QUOTE;
      }
    }
    return libraryPath;
  }

  /**
   * Executes the specified command.
   *
   * @param command the command
   * @param dir the working directory
   *
   * @throws IOException on an I/O error
   *
   * @return started process
   */
  public static Process execProcess(String command, File dir) throws
                                                                  IOException {
    return Runtime.getRuntime().exec(command, null, dir);
  }

// Package-Private Static Methods. ---------------------------------------------

  static String findJavaCommand() {
    if (verifyJVM(STRING_JAVAW)) {
      return STRING_JAVAW;
    }
    if (verifyJVM(STRING_JAVA)) {
      return STRING_JAVA;
    }
    if (verifyJVM(STRING_GIJ)) {
      return STRING_GIJ;
    }
    if (testJVM(STRING_JAVAW)) {
      return STRING_JAVAW;
    }
    if (testJVM(STRING_JAVA)) {
      return STRING_JAVA;
    }
    if (testJVM(STRING_GIJ)) {
      return STRING_GIJ;
    }
    throw new RuntimeException(STRING_NO_JAVA_COMMAND); // NOPMD
  }

  static boolean verifyJVM(String jvm) {
    BufferedReader isr = null;
    try {
      Process p = execProcess(jvm + STRING_SPACE + STRING_CLASSPATH +
                              getClassPath() + STRING_SPACE +
                              ExecTestSuite.class.getName() +
                              STRING_SPACE + STRING_JAVA_HOME, null);
      if (p.waitFor() == 0) {
        isr = new BufferedReader(new InputStreamReader(p.getInputStream()));
        return getJavaHome().equals(isr.readLine()); // NOFB
      }
    } catch (Throwable t) { // NOPMD
      // empty catch block
    } finally {
      closeReader(isr);
    }
    return false;
  }

  static boolean testJVM(String jvm) {
    try {
      execProcess(jvm + " -version", null);
      return true;
    } catch (Throwable t) {
      return false;
    }
  }

  static boolean isWinPath(String path) {
    return (path.indexOf(":\\") > 0); // NOFB
  }

  static void closeStream(InputStream is) {
    if (is != null) {
      try {
        is.close();
      } catch (IOException ioe) {
        System.err.println("Error closing stream"); // NOPMD
        ioe.printStackTrace(); // NOPMD
      }
    }
  }

  static void closeStream(OutputStream os) {
    if (os != null) {
      try {
        os.close();
      } catch (IOException ioe) {
        System.err.println("Error closing stream"); // NOPMD
        ioe.printStackTrace(); // NOPMD
      }
    }
  }

  static void closeReader(Reader r) {
    if (r != null) {
      try {
        r.close();
      } catch (IOException ioe) {
        System.err.println("Error closing reader"); // NOPMD
        ioe.printStackTrace(); // NOPMD
      }
    }
  }

}
